﻿Option Explicit On
'*******************************************************************
' Program: pkgVisio
' Author: Albert E Edlund
' Date:
'
' Purpose:
' Working with office object XML
'
'*******************************************************************


#Region " imports "

Imports System
Imports System.Object
Imports System.IO
Imports System.IO.TextReader
' need to reference windowsbase in .net
Imports System.IO.Packaging

Imports System.Data.Common

Imports System.Windows
Imports System.Windows.Forms
Imports System.Text.StringBuilder

Imports Microsoft.VisualBasic

Imports System.Text
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel

Imports System.Xml
Imports System.Linq
Imports System.Linq.Enumerable
Imports System.Windows.Input

Imports System.Xml.Serialization
Imports System.Xml.Linq
Imports System.Xml.XPath


Imports Microsoft.Practices.EnterpriseLibrary.Common
Imports Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
Imports Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging


' visio v15 does not use a xName prefix which may put it into conflict with other open office apps
Imports <xmlns="http://schemas.microsoft.com/office/visio/2012/main">
Imports <xmlns:rs="urn:schemas-microsoft-com:rowset">

' generic namespace for office open xml, used in all of them
Imports <xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">

Imports pkgVisio.clsVisPackage
Imports pkgVisio.clsShape
Imports pkgVisio.clsStructures

#End Region



'**********************************************************************************
'
'**********************************************************************************
'
Public Class clsPage


#Region " Properties and Declarations "

    '
    '
    '
    Public m_visPackage As clsVisPackage
    Public Property visPackage As clsVisPackage
        Get
            visPackage = Me.m_visPackage
        End Get
        Set(value As clsVisPackage)
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "visPackage is Null.")
            End If
            Me.m_visPackage = value
        End Set
    End Property

    '
    ' 
    '
    Public _xDoc As XDocument
    Public Property xDoc As XDocument
        Get
            Return Me._xDoc
        End Get
        Set(value As XDocument)
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "xDoc is Null.")
            End If
            Me._xDoc = value
        End Set
    End Property


    '
    '
    '
    Private _strPgName As String
    Public Property strPgName As String
        Get
            strPgName = _strPgName
        End Get
        Set(value As String)
            _strPgName = value
        End Set
    End Property

    '
    '
    '
    Private _strPgNameU As String
    Public Property strPgNameU As String
        Get
            strPgNameU = _strPgNameU
        End Get
        Set(value As String)
            _strPgNameU = value
        End Set
    End Property

    '
    '
    '
    Private _lngPgId As Long
    Public Property lngPgId As Long
        Get
            lngPgId = Me._lngPgId
        End Get
        Set(value As Long)
            Me._lngPgId = value
        End Set
    End Property

    '
    '
    '
    Private _PgSource As XElement
    Public Property PgSource As XElement
        Get
            PgSource = _PgSource
        End Get
        Set(value As XElement)
            _PgSource = value
        End Set
    End Property

#End Region

#Region " Test Strings "


    '
    ' had to add namespace to page definition because of prefix issues with relationship id
    ' decided to copy how the master template default is
    '
    Public Const pageTemplateXML As String = _
        "<Page ID=""0"" NameU=""Page-1"" Name=""Page-1"" ViewScale=""-1"" " _
        & "ViewCenterX=""4.2592592592593"" ViewCenterY=""5.5277777777778"" " _
        & "xmlns=""http://schemas.microsoft.com/office/visio/2012/main"">" _
        & "<PageSheet LineStyle=""0"" FillStyle=""0"" TextStyle=""0""> " _
        & "<Cell N=""PageWidth"" V=""8.5""/> " _
        & "<Cell N=""PageHeight"" V=""11""/> " _
        & "<Cell N=""ShdwOffsetX"" V=""0.125""/> " _
        & "<Cell N=""ShdwOffsetY"" V=""-0.125""/> " _
        & "<Cell N=""PageScale"" V=""1"" U=""IN_F""/> " _
        & "<Cell N=""DrawingScale"" V=""1"" U=""IN_F""/> " _
        & "<Cell N=""DrawingSizeType"" V=""0""/> " _
        & "<Cell N=""DrawingScaleType"" V=""0""/> " _
        & "<Cell N=""InhibitSnap"" V=""0""/> " _
        & "<Cell N=""PageLockReplace"" V=""0"" U=""BOOL""/> " _
        & "<Cell N=""UIVisibility"" V=""0""/> " _
        & "<Cell N=""ShdwType"" V=""0""/> " _
        & "<Cell N=""ShdwObliqueAngle"" V=""0""/> " _
        & "<Cell N=""ShdwScaleFactor"" V=""1""/> " _
        & "<Cell N=""DrawingResizeType"" V=""1""/> " _
        & "<Cell N=""PinX"" V=""0""/>" _
        & "<Cell N=""PinY"" V=""0""/>" _
        & "<Cell N=""Width"" V=""1""/>" _
        & "<Cell N=""Height"" V=""1""/>" _
        & "<Cell N=""LocPinX"" V=""0"" F=""Width*0""/>" _
        & "<Cell N=""LocPinY"" V=""0"" F=""Height*0""/>" _
        & "<Cell N=""Angle"" V=""0""/>" _
        & "<Cell N=""FlipX"" V=""0""/>" _
        & "<Cell N=""FlipY"" V=""0""/>" _
        & "<Cell N=""ResizeMode"" V=""0""/>" _
        & "<Cell N=""RouteStyle"" V=""2"" />" _
        & "<Cell N=""PageShapeSplit"" V=""1""/>" _
        & "</PageSheet> " _
        & "<Rel r:id=""rId1"" xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" />" _
        & "</Page> "

    Public Const pageHeaderXML As String = _
        "<?xml version=""1.0"" encoding=""utf-8"" ?> " _
        & "<PageContents xmlns=""http://schemas.microsoft.com/office/visio/2012/main"" " _
        & "xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" " _
        & "xml:space=""preserve""> " _
        & "</PageContents>"


#End Region

    '
    '
    '
    Public Sub loadPageXDoc _
        (ByVal pkgSource As clsVisPackage, _
         ByVal strPgFile As String)

        Me.visPackage = pkgSource

        Try
            If pkgSource.colPkgPartInfo.Count = 0 Then
                pkgSource.loadPkgAndParts()
            End If
            If pkgSource.colPkgPartInfo.Count > 0 Then
                For Each node As pkgpartInfo In pkgSource.colPkgPartInfo

                    If node.pkgpartContentType = clsVisPackage.contXmlPage _
                        And node.pkgpartPath = strPgFile _
                        And node.pkgpartParent = "/visio/pages/pages.xml" Then

                        Me.xDoc = pkgSource.getPackagePartAsXDoc(Me.visPackage.fileCurrent, node)

                        existsSectionConnects()

                    End If ' test for node name
                Next node
            End If ' test for collection valid

        Catch errXml As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(errXml, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub

    '
    '
    '
    Public Sub addPageToPkgPart _
        (ByRef package As Package, _
         ByRef pkgpartPages As PackagePart, _
         ByVal strPagePath As String, _
         ByVal strNewRid As String, _
         Optional ByVal strPageContent As String = clsPage.pageHeaderXML)

        Try
            Const bufSize As Integer = &H1000
            Dim uniEncoding As New UTF8Encoding
            Dim byteXML As Byte()

            ' Relative path and filename of the app file to add to the package.
            Dim pagePath As String = "/visio/pages/" & strPagePath
            Dim partUriPage As Uri = PackUriHelper.CreatePartUri(New Uri(pagePath, UriKind.Relative))
            Dim packagePartPage As PackagePart = package.CreatePart(partUriPage, clsVisPackage.contXmlPage)
            ' load the byte array
            byteXML = uniEncoding.GetBytes(strPageContent)
            Using memStream As New MemoryStream(bufSize)
                memStream.Write(byteXML, 0, byteXML.Length)
                Me.visPackage.CopyStream(memStream, packagePartPage.GetStream())
            End Using ' close the memory stream
            pkgpartPages.CreateRelationship(packagePartPage.Uri, TargetMode.Internal, clsVisPackage.relRidPage, strNewRid)

            GC.Collect()


        Catch xml As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(xml, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try



    End Sub

    '
    '
    '
    Public Function addShapesXelementToXDoc() As XElement

        Dim xelemReturn As XElement = Nothing
        Dim blnShapes As Boolean = False

        ' start the element drilldown
        Dim listOfElements As IEnumerable(Of XElement) = _
                From elem In Me.xDoc.Elements() _
                Select elem
        For Each elem As XElement In listOfElements
            Dim strName As String = LCase(elem.Name.LocalName)
            If strName = "shapes" Then
                blnShapes = True
                xelemReturn = elem
            End If
        Next elem

        If blnShapes = False Then
            xelemReturn = New XElement("Shapes")
            Me.xDoc.Root.Add(xelemReturn)
        End If

        Return xelemReturn

    End Function


#Region " Connects "


    '<Connects>
    '   <Connect FromSheet="45" FromCell="BeginX" FromPart="9" ToSheet="11" ToCell="Connections.Bottom.X" ToPart="104" /> 
    '   <Connect FromSheet="45" FromCell="EndX" FromPart="12" ToSheet="13" ToCell="Connections.Top.X" ToPart="103" /> 
    '   <Connect FromSheet="46" FromCell="BeginX" FromPart="9" ToSheet="13" ToCell="Connections.Bottom.X" ToPart="104" /> 
    '   <Connect FromSheet="46" FromCell="EndX" FromPart="12" ToSheet="16" ToCell="Connections.Top.X" ToPart="103" /> 
    '   <Connect FromSheet="47" FromCell="BeginX" FromPart="9" ToSheet="13" ToCell="Connections.Right.X" ToPart="102" /> 
    '   <Connect FromSheet="47" FromCell="EndX" FromPart="12" ToSheet="14" ToCell="Connections.Left.X" ToPart="101" /> 
    '</Connects>

    '<xsd:complexType name="Connect_Type">
    '   <xsd:attribute name="FromSheet" type="xsd:unsignedInt" use="required" />
    '   <xsd:attribute name="FromCell" type="xsd:string" />
    '   <xsd:attribute name="FromPart" type="xsd:int" />
    '   <xsd:attribute name="ToSheet" type="xsd:unsignedInt" use="required" />
    '   <xsd:attribute name="ToCell" type="xsd:string" />
    '   <xsd:attribute name="ToPart" type="xsd:int" />
    '</xsd:complexType>

    '    The following constants declared by the Microsoft Office Visio type 
    '       library show return values for the FromPart property.
    'Constant  Value  
    'visConnectFromError         -1
    'visFromNone                 0
    'visLeftEdge                 1
    'visCenterEdge               2
    'visRightEdge                3
    'visBottomEdge               4
    'visMiddleEdge               5
    'visTopEdge                  6
    'visBeginX                   7
    'visBeginY                   8
    'visBegin                    9
    'visEndX                     10
    'visEndY                     11
    'visEnd                      12
    'visFromAngle                13
    'visFromPin                  14
    'visControlPoint             100 + zero-based row index (for example, visControlPoint = 100 if the control point is in row 0; visControlPoint = 101 if the control point is in row 1)


    '    The ToPart property identifies the part of a shape to which another 
    '    shape is glued, such as its begin point or endpoint, one of its edges, 
    '    or a connection point. The following constants declared by the Visio type library in member VisToParts show possible return values for the ToPart property.
    'Constant               Value  
    'visConnectToError       -1
    'visToNone               0
    'visGuideX               1
    'visGuideY               2
    'visWholeShape           3
    'visGuideIntersect       4
    'visToAngle              7
    'visConnectionPoint      100 + row index of connection point

    '
    '
    '
    Public arrConnectsFields() As String = _
    {"FromSheet", "FromCell", "FromPart", "ToSheet", "ToCell", "ToPart"}


    '
    '
    '
    Public Sub addConnectToConnects _
        (ByVal uintFromSheet As UInt16, _
         ByVal strFromCell As String, _
         ByVal intFromPart As Int16, _
         ByVal uintToSheet As UInt16, _
         ByVal strToCell As String, _
         ByVal intToPart As Int16)

        Try

            Dim xelemConnects As XElement = Nothing
            Dim qryConnects = From connSel In Me.xDoc.Descendants _
                    Where connSel.Name = "Connects" _
                    Select connSel

            If qryConnects IsNot Nothing Then
                If 0 < qryConnects.Count Then
                    xelemConnects = qryConnects.FirstOrDefault
                End If
            Else
                xelemConnects = addConnectsXelementToXDoc()
            End If

            If xelemConnects IsNot Nothing Then
                Dim xelemNew As XElement = New XElement("Connect")
                xelemNew.SetAttributeValue("FromSheet", CStr(uintFromSheet))
                xelemNew.SetAttributeValue("FromCell", strFromCell)
                xelemNew.SetAttributeValue("FromPart", CStr(intFromPart))
                xelemNew.SetAttributeValue("ToSheet", CStr(uintToSheet))
                xelemNew.SetAttributeValue("ToCell", strToCell)
                xelemNew.SetAttributeValue("ToPart", CStr(intToPart))
                xelemConnects.Add(xelemNew)
            End If

        Catch xml As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(xml, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try


    End Sub

    '
    ' in case the connects element is not in place
    '
    Public Function addConnectsXelementToXDoc() As XElement

        Dim xelemReturn As XElement = existsSectionConnects()
        Dim blnShapes As Boolean = False



        If xelemReturn Is Nothing Then
            xelemReturn = New XElement("Connects")
            Me.xDoc.Root.Add(xelemReturn)
        End If

        Return xelemReturn

    End Function


    '
    '
    '
    Public Function existsSectionConnects() As XElement

        Dim xelemSection As XElement = Nothing


        Dim qryConnects = From connSel In Me.xDoc.Descendants _
                Where connSel.Name = "Connects" _
                Select connSel

        If qryConnects IsNot Nothing Then
            If 0 < qryConnects.Count Then
                xelemSection = qryConnects.FirstOrDefault
            End If
        End If

        Return xelemSection

    End Function


#End Region


#Region " Shapes "

    '
    '
    '
    Public Function existsShape(ByVal strShapeName As String) As XElement

        Dim xelemShape As XElement = Nothing

        Try

            ' go find a shape with a NameU 
            Dim qryShape = From shpSel In Me.xDoc.Descendants
                Where shpSel.Name = "Shape" And shpSel.@NameU = strShapeName _
                Select shpSel

            If qryShape.Count = 1 Then
                xelemShape = qryShape.FirstOrDefault
            End If

        Catch xml As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(xml, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
        Return xelemShape

    End Function



#End Region


    '
    '
    '
    Public Sub New()

    End Sub

    '
    '
    '
    Public Sub New(ByVal newPackage As clsVisPackage, ByVal newPage As XDocument)

        Me.m_visPackage = Nothing
        Me.visPackage = New clsVisPackage
        Me.visPackage = newPackage

        Me._xDoc = Nothing
        Me.xDoc = New XDocument
        Me.xDoc = newPage

    End Sub



    '
    '
    '
    Protected Overrides Sub Finalize()

        If Me.m_visPackage IsNot Nothing Then
            Me.m_visPackage = Nothing
        End If

        MyBase.Finalize()
    End Sub


End Class

